/*
 * Copyright (c) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import prompt from '@system.prompt';
import dataStorage from '@ohos.data.storage'
import wantConstant from '@ohos.ability.wantConstant';
import featureAbility from '@ohos.ability.featureAbility'
import HuaweiAccount from '../../../share/common/scripts/HuaweiAccount.js'
import state from '../../../share/common/state.js';
import config from '../../../share/common/config.js';
import app from '@system.app'
import {HealthEcologyOperator as hekOperator, BleNotifyType} from '@health-ecology-kit/hek-operator';
import permissionHelper from '../../../share/common/scripts/permissionHelper.js';

const JS_TAG = 'JS/Page/Connection: ';

export default {
  data: {
    deviceConnected: false,
    state: state.connection.PRIVACY,
  },
////////////////////////////////////////
////////        页面初始化       ////////
  onInit() {
    console.debug(JS_TAG + 'Page init started.');
    this.checkLoginAndPrivacy();
    this.initDeviceParams();
    this.initBleListeners();
  },
  initDeviceParams() {
    this.$app.$def.globalData.isDarkMode = this.isDarkMode;
    this.$app.$def.globalData.bundleName = this.bundleName;
    this.productId = this.productId ? this.productId : config.customDefaultData.productId;
    this.macAddress = this.macAddress ? this.macAddress : config.customDefaultData.deviceMac;
  },
  initBleListeners() {
    hekOperator.onBleDevicesFound(this.bleDeviceFoundCallback);
    hekOperator.onBleServiceDiscovered(this.bleServiceDiscoveredCallback);
    hekOperator.onBleConnectionStateChange(this.bleConnectionStateCallback);
    hekOperator.onBluetoothAdapterStateChange(this.bluetoothAdapterStateCallback);
    hekOperator.onBleCharacteristicValueChange(this.bleCharacteristicChangeCallback);
  },
  //////////////////////////////////////////
  ////////      隐私同意与账号登陆      ///////
  async checkLoginAndPrivacy() {
    console.info(JS_TAG + 'Checking login and privacy status');
    let path = await featureAbility.getContext().getFilesDir();
    let storage = dataStorage.getStorageSync(path + '/preferences');
    let expireTimestamp = storage.getSync('rt_expire_timestamp', '0');
    let timestampNow = new Date().getTime();
    if (timestampNow < expireTimestamp) {
      // Refresh Token not expired
      console.debug(JS_TAG + 'Refresh token not expired!')
      this.onLoginFinished();
    } else {
      // Refresh Token Expired
      prompt.showToast({
        message: this.$t('strings.public_logged_out_message')
      });
      console.debug(JS_TAG + 'Refresh token expired!');
    }
  },
  privacyButtonClick() {
    console.debug(JS_TAG + 'onPrivacyButtonClick');
    this.changeState(state.connection.PRIVACY_CONTENT, state.connection.PRIVACY);
  },
  onPrivacyConfirmed() {
    console.debug(JS_TAG + 'onPrivacyConfirmed.');
    this.changeState(state.connection.PRIVACY, state.connection.PRIVACY_CONTENT);
  },
  onLoginSuccess(resultObj) {
    console.debug(JS_TAG + 'Login success:' + JSON.stringify(resultObj));
    if (resultObj.detail.allScopeGranted) {
      this.onLoginFinished();
    } else {
      prompt.showToast({
        message: this.$t('strings.public_scope_denied'),
        duration: config.toastDuration
      });
      this.changeState(state.connection.PRIVACY);
    }
  },
  onLoginError(loginError) {
    console.error(JS_TAG + 'onLoginError, error = ' + JSON.stringify(loginError));
    this.changeState(state.connection.PRIVACY, state.connection.DISCOVERING, state.connection.CONNECTING);
  },
  async onLoginFinished() {
    this.changeState(state.connection.DISCOVERING, state.connection.PRIVACY);
    if (config.proxyRegisterRequired && !this.deviceId && this.uuid && this.ProtocolID == 2) {
      //  with proxyRegisterRequired flag is true,
      //  with sufficient nfc tag(protocolId == 2),
      //  with deviceId empty(not registered),
      //  with uuid exists(for hilink authentication, carried by hilink at startup)
      let startAbilityOption = {
        want: {
          uri: 'hilink://hilinksvc.huawei.com/device?action=deviceAdd&prodId='
          + this.productId + '&fromApp=' + this.bundleName,
          flags: wantConstant.Flags.FLAG_ABILITY_NEW_MISSION | wantConstant.Flags.FLAG_NOT_OHOS_COMPONENT,
          parameters: {
            uuid: this.uuid
          }
        }
      }
      featureAbility.startAbility(startAbilityOption);
      app.terminate();
    } else {
      this.checkBluetoothAdapterState();
    }

  },
  //////////////////////////////////////////
  ////////      位置权限检查与授予      ///////
  checkAndRequestPermissions() {
    console.debug(JS_TAG + 'Scan required is :' + config.scanRequired);
    if (config.scanRequired) {
      console.debug(JS_TAG + 'Location permission is required for scanning, start checking and requiring...');
      permissionHelper.checkAndRequestPermission()
        .then(this.onPermissionGranted)
        .catch(this.onPermissionDenied);
    } else {
      this.onPermissionGranted();
    }
  },
  onPermissionGranted() {
    console.debug(JS_TAG + 'Permission Granted!');
    this.startDeviceDiscovery();
  },
  onPermissionDenied(permissionRequestResult) {
    console.error(JS_TAG + 'Permission Denied!' + JSON.stringify(permissionRequestResult));
    var updateState = permissionRequestResult.isPermissionForbidden
      ? state.connection.PERMISSION_FORBIDDEN
      : state.connection.PERMISSION_DENIED;
    this.changeState(updateState);
  },
  ////////////////////////////////////////
  ////////        蓝牙接口调用       ///////
  checkBluetoothAdapterState() {
    hekOperator.getBluetoothAdapterState({
      success: this.onCheckAdapterStateSuccess,
      fail: this.onCheckAdapterStateFailed
    });
  },
  startDeviceDiscovery() {
    hekOperator.startBleDevicesDiscovery({
      isAllowDuplicatesKey: false,
      reportInterval: 0, //  Report the device as soon as it is found
      timeoutInMillis: 10000,
      uuidServices: config.scanFilterUuid,
      success: this.onStartDiscoverySuccess,
      fail: this.onDeviceDiscoveryFailed
    });
  },
  stopDevicesDiscovery() {
    hekOperator.stopBleDevicesDiscovery({
      success: () => {
        console.debug(JS_TAG + 'Stop bluetooth device discovery success!');
      },
      fail: this.onStopDeviceDiscoveryFailed
    });
  },
  connectToDevice(deviceId) {
    hekOperator.createBleConnection({
      deviceId: deviceId,
      isAutoConnect: false,
      success: this.onStartConnectSuccess,
      fail: this.onDeviceConnectFailed
    })
  },
  reConnect() {
    console.debug(JS_TAG + 'Reconnect!')
    this.checkBluetoothAdapterState();
  },
  enableNotifyAndIndicate() {
    hekOperator.notifyBleCharacteristicValueChange({
      deviceId: this.macAddress,
      serviceId: config.serviceUuid,
      characteristicId: config.characterUuid,
      notifyType: BleNotifyType.INDICATION, // NOTIFICATION for notify, INDICATION for indicate
      success: () => {
        console.debug(JS_TAG + 'Enable notify and indicate success!');
      },
      fail: this.onEnableNotifyFailed
    });
  },
  ////////////////////////////////////////
  ////////        蓝牙回调处理       ///////
  bluetoothAdapterStateCallback(adapterState) {
    if (adapterState.isAvailable &&
    (this.state == state.connection.RECONNECT_REQUESTED || this.state == state.connection.REDISCOVER_REQUESTED)) {
      this.onAdapterAvailable();
    } else if (!adapterState.isAvailable &&
    (this.state == state.connection.DISCOVERING || this.state == state.connection.CONNECTING)) {
      this.onAdapterNotAvailable();
    } else {
      console.debug(JS_TAG + 'Ignore the adapter state change.');
    }
  },
  bleConnectionStateCallback(connectionState) {
    console.debug(JS_TAG + 'Connection state changed: ' + JSON.stringify(connectionState));
    this.deviceConnected = connectionState.isConnected;
    if (!this.deviceConnected) {
      this.onDeviceConnectFailed();
    }
  },
  bleServiceDiscoveredCallback(bleServiceDiscoveredResult) {
    (this.deviceConnected && bleServiceDiscoveredResult.isDiscoverySuccess)
      ? this.onServiceDiscoveredSuccess()
      : this.onServiceDiscoveredFailed();
  },
  bleDeviceFoundCallback(foundDevices) {
    console.debug(JS_TAG + 'bleDeviceFoundCallback: ' + JSON.stringify(foundDevices));
    if (foundDevices.length == 0 && this.state == state.connection.DISCOVERING) {
      this.onDeviceDiscoveryFailed(undefined, 'Discovery timeout.');
    }
    for (let device of foundDevices) {
      if (device.deviceId == this.macAddress) {
        this.onTargetDeviceFound(device.deviceId);
        return;
      }
    }
  },
  bleCharacteristicChangeCallback(bleCharacteristicChangeValue) {
    console.debug(JS_TAG + 'change data: ' + JSON.stringify(bleCharacteristicChangeValue.characteristicData));
  },
  ////////////////////////////////////////
  ////////        页面流程处理       ///////
  onBackPress() {
    console.debug(JS_TAG + 'On back pressed.');
    if (this.state == state.connection.PRIVACY_CONTENT) {
      this.changeState(state.connection.PRIVACY, state.connection.PRIVACY_CONTENT);
      return true
    }
  },
  updateStateToConnectFailed() {
    this.changeState(state.connection.CONNECT_FAILED);
  },
  onDeviceConnectFailed(errorCode, errorMessage) {
    console.error(JS_TAG +
      'Create ble connection error, errorCode is ' + errorCode + ', message is ' + JSON.stringify(errorMessage));
    this.updateStateToConnectFailed();
  },
  onDeviceDiscoveryFailed(errorCode, errorMessage) {
    console.error(JS_TAG +
      'Discover device error, errorCode is ' + errorCode + ', message is ' + JSON.stringify(errorMessage));
    this.deviceConnected = false;
    this.changeState(state.connection.DISCOVER_FAILED);
  },
  onServiceDiscoveredFailed() {
    console.error(JS_TAG + 'BleService discovers failed.');
    this.updateStateToConnectFailed();
  },
  onEnableNotifyFailed(errorCode, errorMessage) {
    console.error(JS_TAG +
      'Enable notify or indicate error, errorCode is ' + errorCode + ', message is ' + JSON.stringify(errorMessage));
    this.updateStateToConnectFailed();
  },
  onAdapterOpenFailed(errorCode, errorMessage) {
    console.error(JS_TAG +
      'Open bluetooth adapter error, errorCode is ' + errorCode + ', message is ' + JSON.stringify(errorMessage));
    this.updateStateToConnectFailed();
  },
  onStopDeviceDiscoveryFailed(errorCode, errorMessage) {
    console.error(JS_TAG +
      'Stop bluetooth discovery error, errorCode is ' + errorCode + ', message is ' + JSON.stringify(errorMessage));
  },
  onCheckAdapterStateFailed(errorCode, errorMessage) {
    console.error(JS_TAG +
      'Get bluetooth adapter state error, errorCode is ' + errorCode + ', message is ' + JSON.stringify(errorMessage));
  },
  onStartDiscoverySuccess() {
    console.debug(JS_TAG + 'Start discover device success!');
    this.changeState(state.connection.DISCOVERING);
  },
  onTargetDeviceFound(deviceId) {
    this.stopDevicesDiscovery();
    this.connectToDevice(deviceId);
  },
  onStartConnectSuccess() {
    console.debug(JS_TAG + 'Create ble connection success!');
    this.changeState(state.connection.CONNECTING);
  },
  onServiceDiscoveredSuccess() {
    this.toDashboard();
  },
  onCheckAdapterStateSuccess(bluetoothAdapterState) {
    console.debug(JS_TAG + 'Bluetooth adapter state is:' + JSON.stringify(bluetoothAdapterState));
    bluetoothAdapterState.isAvailable ? this.onAdapterAvailable() : this.onCheckAdapterNotAvailable();
  },
  onAdapterAvailable() {
    if (config.scanRequired) {
      this.checkAndRequestPermissions();
    } else {
      this.connectToDevice(this.macAddress);
    }
  },
  onCheckAdapterNotAvailable() {
    this.changeState(state.connection.RECONNECT_REQUESTED, state.connection.CONNECT_FAILED);
    this.changeState(state.connection.REDISCOVER_REQUESTED, state.connection.DISCOVER_FAILED);
    hekOperator.openBluetoothAdapter({
      success: () => {
        console.debug(JS_TAG + 'Open bluetooth adapter success!');
      },
      fail: this.onAdapterOpenFailed
    });
  },
  onAdapterNotAvailable() {
    console.error(JS_TAG + 'bluetooth adapter not available.');
    this.changeState(state.connection.DISCOVER_FAILED, state.connection.DISCOVERING);
    this.changeState(state.connection.CONNECT_FAILED, state.connection.CONNECTING);
    this.stopDevicesDiscovery();
  },
  toDashboard() {
    console.debug(JS_TAG + 'Connected, jumping to dashboard.');
    let startAbilityOption = {
      want: {
        bundleName: this.bundleName,
        abilityName: this.abilityName,
        flags: wantConstant.Flags.FLAG_ABILITY_NEW_MISSION,
        parameters: {
          params: JSON.stringify({
            macAddress: this.macAddress,
            productId: this.productId,
          }),
          restartRequired: true
        }
      }
    }
    featureAbility.startAbility(startAbilityOption).then(result => {
      console.debug(JS_TAG + 'startAbilityOption success. Data: ' + JSON.stringify(result));
    });
  },
  changeState(newState, ...oldStates) {
    // 使用此函数以在状态之间进行转换，对应状态将显示对应的页面。
    console.debug('Trying change state from: ' + oldStates + ' to: ' + newState);
    if (oldStates == undefined || oldStates.length == 0 || oldStates.indexOf(this.state) > -1) {
      console.debug('State changed from: ' + this.state + ' to: ' + newState);
      this.state = newState;
    } else {
      console.error('Can not change from: ' + this.state + ' to: ' + newState);
    }
  }
}
